Passed
Pull Request — master (#17)
by
unknown
02:50
created

LevelStorage.decrease   A

Complexity

Conditions 1

Size

Total Lines 3
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 3
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
1
import { event, select } from 'd3-selection';
2
import { DependencyNode, TreeNode } from '../../components/types';
3
import {
4
    centerScreenToDimension,
5
    changeZoom,
6
    findGroupBackgroundDimension,
7
    findMaxDependencyLevel,
8
    getHighLightedLabelColor,
9
    hideHighlightBackground,
10
    highlight,
11
    zoomToHighLightedNodes,
12
} from './GraphHelpers';
13
import { LabelColors, MAXIMUM_ZOOM_SCALE, MINIMUM_ZOOM_SCALE, Selectors, TextColors, ZOOM_DECREASE, ZOOM_INCREASE } from '../AppConsts';
14
import { zoom, zoomIdentity } from 'd3-zoom';
15
import { selectAllNodes, selectDetailsButtonWrapper, selectDetailsExitButtonWrapper, selectHighLightedNodes } from './Selectors';
16
import { initializeDetailsView, shutdownDetailsView } from './DetailsDrawHelpers';
17
18
enum Subscriptions {
19
    HIGHLIGHT = 'click.highlight',
20
    RESET_HIGHLIGHT = 'click.resetHighlight',
21
    CHANGE_HIGHLIGHT_RANGE = 'keydown.changeHighlightRange',
22
    ZOOM_ON_ARROW_KEY = 'keydown.zoom',
23
    OPEN_DETAILS = 'click.openDetails',
24
    CLOSE_DETAILS = 'click.closeDetails',
25
}
26
27
export function subscribeToHighlight() {
28
    selectAllNodes().on(Subscriptions.HIGHLIGHT, (node: DependencyNode) => {
29
        LevelStorage.reset();
30
        if (node.links.length) {
31
            highlight(node);
32
            zoomToHighLightedNodes();
33
        }
34
        event.stopPropagation();
35
    });
36
}
37
38
export function subscribeToChangeHighlightRangeOnArrowKey() {
39
    select('body').on(Subscriptions.CHANGE_HIGHLIGHT_RANGE, () => {
40
        if (event.code === 'ArrowRight' || event.code === 'ArrowLeft') {
41
            const labelNodesGroup = select<SVGGElement, DependencyNode>('g#labels');
42
            LevelStorage.setMaxLevel(findMaxDependencyLevel(labelNodesGroup));
43
44
            if (!isFinite(LevelStorage.getMaxLevel())) {
45
                return;
46
            }
47
48
            if (LevelStorage.isBelowMax() && event.code === 'ArrowRight') {
49
                LevelStorage.increase();
50
            }
51
52
            if (LevelStorage.isAboveMin() && event.code === 'ArrowLeft') {
53
                LevelStorage.decrease();
54
            }
55
56
            // TODO refactor it to share logic with GraphHelpers/highlight function
57
            labelNodesGroup
58
                .selectAll<HTMLElement, DependencyNode>('g')
59
                .filter((node: DependencyNode) => node.level > 0)
60
                .each(function(this: HTMLElement, node: DependencyNode) {
61
                    const labelElement = this.firstElementChild;
62
                    const textElement = this.lastElementChild;
63
64
                    if (!labelElement || !textElement) {
65
                        return;
66
                    }
67
68
                    let labelColor = LabelColors.DEFAULT;
69
                    let textColor = TextColors.DEFAULT;
70
                    if (node.level - 1 <= LevelStorage.getLevel()) {
71
                        labelColor = getHighLightedLabelColor(node);
72
                        textColor = TextColors.HIGHLIGHTED;
73
                    }
74
75
                    select<Element, DependencyNode>(labelElement).attr('fill', labelColor);
76
                    select<Element, DependencyNode>(textElement).style('fill', textColor);
77
                });
78
79
            zoomToHighLightedNodes();
80
        }
81
    });
82
}
83
84
export function subscribeToResetHighlight() {
85
    select(Selectors.CONTAINER).on(Subscriptions.RESET_HIGHLIGHT, () => {
86
        const highlightedNodes = selectHighLightedNodes();
87
        if (highlightedNodes.data().length) {
88
            selectAllNodes().each((node: DependencyNode) => (node.level = 0));
89
90
            const dimension = findGroupBackgroundDimension(highlightedNodes.data());
91
92
            highlightedNodes.each(function(this: SVGGElement) {
93
                const labelElement = this.firstElementChild;
94
                const textElement = this.lastElementChild;
95
96
                if (!labelElement || !textElement) {
97
                    return;
98
                }
99
100
                select<Element, DependencyNode>(labelElement).attr('fill', LabelColors.DEFAULT);
101
                select<Element, DependencyNode>(textElement).style('fill', TextColors.DEFAULT);
102
            });
103
104
            hideHighlightBackground();
105
106
            centerScreenToDimension(dimension, 1);
107
        }
108
    });
109
}
110
111
export function subscribeToZoomOnArrowKey() {
112
    select('body').on(Subscriptions.ZOOM_ON_ARROW_KEY, () => {
113
        switch (event.code) {
114
            case 'ArrowUp': {
115
                const currentScaleValue = ZoomScaleStorage.getScale();
116
                const newScaleValue = currentScaleValue * ZOOM_INCREASE;
117
                if (newScaleValue <= MAXIMUM_ZOOM_SCALE) {
118
                    ZoomScaleStorage.setScale(newScaleValue);
119
                    const container = select(Selectors.CONTAINER);
120
                    container.call(() => {
121
                        return zoom<any, any>()
122
                            .on('zoom', changeZoom(Selectors.ZOOM_OVERVIEW))
123
                            .scaleBy(container, ZOOM_INCREASE);
124
                    }, zoomIdentity);
125
                }
126
                break;
127
            }
128
            case 'ArrowDown': {
129
                const currentScaleValue = ZoomScaleStorage.getScale();
130
                const newScaleValue = currentScaleValue * ZOOM_DECREASE;
131
                if (newScaleValue >= MINIMUM_ZOOM_SCALE) {
132
                    ZoomScaleStorage.setScale(newScaleValue);
133
                    const container = select(Selectors.CONTAINER);
134
                    container.call(() => {
135
                        return zoom<any, any>()
136
                            .on('zoom', changeZoom(Selectors.ZOOM_OVERVIEW))
137
                            .scaleBy(container, ZOOM_DECREASE);
138
                    }, zoomIdentity);
139
                }
140
                break;
141
            }
142
        }
143
    });
144
}
145
146
export function subscribeToOpenDetails(detailsNodes: TreeNode[]) {
147
    selectDetailsButtonWrapper().on(Subscriptions.OPEN_DETAILS, () => {
148
        if (selectHighLightedNodes().data().length === 0) {
149
            return;
150
        }
151
        event.stopPropagation();
152
        const selectedNode = selectAllNodes()
153
            .data()
154
            .find(node => node.level === 1);
155
        if (selectedNode) {
156
            const selectedTreeNode = detailsNodes.find(treeNode => treeNode.name === selectedNode.name);
157
            if (selectedTreeNode) {
158
                initializeDetailsView(selectedTreeNode);
159
            }
160
        }
161
    });
162
}
163
164
export function subscribeToCloseDetails() {
165
    selectDetailsExitButtonWrapper().on(Subscriptions.CLOSE_DETAILS, () => {
166
        shutdownDetailsView();
167
    });
168
}
169
170
class LevelStorage {
171
    private static level: number = 1;
172
    private static maxLevel: number = 1;
173
174
    public static getLevel(): number {
175
        return this.level;
176
    }
177
178
    public static increase() {
179
        this.level = this.level + 1;
180
    }
181
182
    public static decrease() {
183
        this.level = this.level - 1;
184
    }
185
186
    public static isBelowMax() {
187
        return this.level < this.maxLevel;
188
    }
189
190
    static isAboveMin() {
191
        return this.level > 1;
192
    }
193
194
    static setMaxLevel(maxLevel: number) {
195
        this.maxLevel = maxLevel;
196
    }
197
198
    static getMaxLevel(): number {
199
        return this.maxLevel;
200
    }
201
202
    public static reset() {
203
        this.level = 1;
204
        this.maxLevel = 1;
205
    }
206
}
207
208
export class ZoomScaleStorage {
209
    private static currentScale = 1;
210
211
    public static setScale(newScale: number) {
212
        this.currentScale = newScale;
213
    }
214
215
    public static getScale() {
216
        return this.currentScale;
217
    }
218
}
219